﻿using System.Linq;
using Myotragus.Data.Domain;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.SqlCommand;

namespace Myotragus.Data
{
  public abstract class LocalizableDefaultsRepository<TId, TEntity, TSaveEntity, TLoadEntity> :
    LocalizableRepository<TId, TEntity, TSaveEntity, TLoadEntity>, 
    ILocalizableDefaultsRepository<TId, TEntity> where TEntity : class, IEntity<TId>
    where TSaveEntity : class, IEntity<TId>, new()
    where TLoadEntity : class, IEntity<TId>
  {
    #region Dependencies

    protected LocalizableDefaultsRepository(ILocalizableEntityDescriptor<TEntity, TSaveEntity, TLoadEntity> descriptor,
      ILocalizableDefaultsEntityProjectionBuilder<TEntity, TSaveEntity, TLoadEntity> projectionsBuilder)
      : base(descriptor, projectionsBuilder)
    {

    }

    #endregion

    public void SetDefaultLcId(int lcId, TId id)
    {
      using (var session = ServiceContainer.OpenSession())
      {
        var save = session.Get<TSaveEntity>(id);

        foreach (var text in Descriptor.LocalizableProperties.Select(prop => 
          (DefaultsSaveLocalizableText) Descriptor.SaveEntityProperties[prop].GetValue(save, ReflectionHelper.ArrObjEmpty)))
          text.DefaultLcId = lcId;
        
        using(var transaction = session.BeginTransaction())
        {
          session.Save(save);
          session.Flush();
          transaction.Commit();
        }
      }
    }

    public override void Save(int lcId, System.Collections.Generic.IEnumerable<TEntity> objs)
    {
      var inserts = objs.Where(o => ! IsUpdate(o.Id)).ToArray();
      
      base.Save(lcId, objs);
      
      foreach (var obj in inserts)
        SetDefaultLcId(lcId, obj.Id);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="criteria"></param>
    /// <param name="lcId"></param>
    /// <param name="joinType"></param>
    /// <remarks>
    /// 
    /// </remarks>
    protected override void AttachAliases(ICriteria criteria, int lcId, JoinType joinType)
    {
      foreach (var join in Descriptor.LocalizableProperties)
      {
        // select * from <table> 
        //  left outer join
        //    <textValues> <join> on <table>.<join>=<textValues>.TextId and 
        //      <join>.LcId=?p0_LcId
        //  inner join
        //    <text> Text<join> on <table>.<join>=Default<join>.TextId
        //      left outer join
        //        <textValues> Default<join> on Text<join>.TextId=Default<join>.TextId and
        //          Text<join>.DefaultLcId=Default<join>.LcId

        var aliasText = EmitHelper.CreateAlias("Text", join);
        var aliasDefault = EmitHelper.CreateAlias("Default", join) ;

        var nestedJoin = string.Format("{0}.Values", aliasText);
        var joinDefault = EmitHelper.CreateDefaultsName(join);

        var restrictionValue = Restrictions.Eq(string.Format("{0}.LcId", join), lcId);
        var restrictionDefault = Restrictions.EqProperty(
          string.Format("{0}.DefaultLcId", aliasText), string.Format("{0}.LcId", aliasDefault));

        criteria
          .CreateAlias(join, join, JoinType.LeftOuterJoin, restrictionValue)
          .CreateAlias(joinDefault, aliasText, JoinType.InnerJoin)
          .CreateAlias(nestedJoin, aliasDefault, JoinType.LeftOuterJoin, restrictionDefault)
          ;          
      }
    }
  }
}